﻿using System;
using Csla.Properties;
using System.Data.SqlClient;
using System.Data.Common;
using Csla;
using Csla.Rules;
using Csla.Core;
using System.ComponentModel;
using System.Data;
using System.Collections.Generic;
using System.Text;
using System.Dynamic;
using Csla.Data;
using System.Net;
using Newtonsoft.Json;
using System.IO;
namespace BO
{
    [Serializable()]
    public abstract class BusinessBaseEx : Csla.Core.BusinessBase, IIsDirty
    {
        protected new void SetProperty<P>(PropertyInfo<P> propertyInfo, P newValue)
        {
            if (object.Equals(this.GetProperty<P>(propertyInfo), newValue))
                return;
            else
                SetProperty<P>(propertyInfo, newValue, Csla.Security.NoAccessBehavior.ThrowException);
        }
        protected new void SetProperty(IPropertyInfo propertyInfo, object newValue)
        {
            if (propertyInfo.Type == typeof(DateTime)&&object.Equals(newValue,"0000-00-00 00:00:00"))
            {
                newValue = new DateTime();
            }
            if (this.GetProperty(propertyInfo).Equals(newValue))
                return;
            base.SetProperty(propertyInfo, newValue);
        }
        public void UpdateObject(BusinessBaseEx pObj)
        {
            var pList = this.FieldManager.GetRegisteredProperties();
            foreach (var p in pList)
            {
                this.SetProperty(p, pObj.GetProperty(p));
            }
        }
        public void MarkClean()
        {
            base.MarkOld();
            base.MarkClean();
        }
        public void MarkNew()
        {
            base.MarkNew();
        }
        #region Object ID Value

        /// <summary>
        /// Override this method to return a unique identifying
        /// value for this object.
        /// </summary>
        /// <remarks>
        /// If you can not provide a unique identifying value, it
        /// is best if you can generate such a unique value (even
        /// temporarily). If you can not do that, then return 
        /// <see langword="Nothing"/> and then manually override the
        /// <see cref="Equals"/>, <see cref="GetHashCode"/> and
        /// <see cref="ToString"/> methods in your business object.
        /// </remarks>
        public virtual object GetIdValue() { return new object(); }

        #endregion

        #region System.Object Overrides

        /// <summary>
        /// Compares this object for equality with another object, using
        /// the results of <see cref="GetIdValue"/> to determine
        /// equality.
        /// </summary>
        /// <param name="obj">The object to be compared.</param>
        //public override bool Equals(object obj)
        //{
        //  if (obj is T)
        //  {
        //    object id = GetIdValue();
        //    if (id == null)
        //      throw new ArgumentException(Resources.ObjectNotNull);
        //    return ((T)obj).GetIdValue().Equals(id);
        //  }
        //  else
        //    return false;
        //}

        /// <summary>
        /// Returns a hash code value for this object, based on
        /// the results of <see cref="GetIdValue"/>.
        /// </summary>
        public override int GetHashCode()
        {
            object id = GetIdValue();
            if (id == null)
                throw new ArgumentException(Resources.ObjectNotNull);
            return id.GetHashCode();
        }

        /// <summary>
        /// Returns a text representation of this object by
        /// returning the <see cref="GetIdValue"/> value
        /// in text form.
        /// </summary>
        public override string ToString()
        {
            object id = GetIdValue();
            if (id == null)
                throw new ArgumentException(Resources.ObjectNotNull);
            return id.ToString();
        }

        #endregion

        #region Clone

        /// <summary>
        /// Creates a clone of the object.
        /// </summary>
        /// <returns>
        /// A new object containing the exact data of the original object.
        /// </returns>
        public virtual BusinessBaseEx Clone()
        {
            return (BusinessBaseEx)GetClone();
        }

        #endregion


        #region Data Access

        /// <summary>
        /// Saves the object to the database.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Calling this method starts the save operation, causing the object
        /// to be inserted, updated or deleted within the database based on the
        /// object's current state.
        /// </para><para>
        /// If <see cref="Core.BusinessBase.IsDeleted" /> is <see langword="true"/>
        /// the object will be deleted. Otherwise, if <see cref="Core.BusinessBase.IsNew" /> 
        /// is <see langword="true"/> the object will be inserted. 
        /// Otherwise the object's data will be updated in the database.
        /// </para><para>
        /// All this is contingent on <see cref="Core.BusinessBase.IsDirty" />. If
        /// this value is <see langword="false"/>, no data operation occurs. 
        /// It is also contingent on <see cref="Core.BusinessBase.IsValid" />. 
        /// If this value is <see langword="false"/> an
        /// exception will be thrown to indicate that the UI attempted to save an
        /// invalid object.
        /// </para><para>
        /// It is important to note that this method returns a new version of the
        /// business object that contains any data updated during the save operation.
        /// You MUST update all object references to use this new version of the
        /// business object in order to have access to the correct object data.
        /// </para><para>
        /// You can override this method to add your own custom behaviors to the save
        /// operation. For instance, you may add some security checks to make sure
        /// the user can save the object. If all security checks pass, you would then
        /// invoke the base Save method via <c>base.Save()</c>.
        /// </para>
        /// </remarks>
        /// <returns>A new object containing the saved values.</returns>
        public virtual Csla.Core.BusinessBase Save()
        {
            if (this.IsChild)
                throw new NotSupportedException(Resources.NoSaveChildException);
            if (EditLevel > 0)
                throw new ValidationException(Resources.NoSaveEditingException);
            if (!IsValid)
                throw new ValidationException(Resources.NoSaveInvalidException);
            if (IsDirty)
            {
                BeforeUpdate();
                return DataPortal.Update(this);

            }
            else
                return this;
        }

        /// <summary>
        /// Used in HAND-CODE part file when before call the DataPortal.Update().
        /// Not used in the Object.Designer.cs
        /// </summary>
        protected virtual void BeforeUpdate()
        {
        }
        protected virtual void AfterUpdate()
        {
        }

        /// <summary>
        /// Saves the object to the database, forcing
        /// IsNew to <see langword="false"/> and IsDirty to True.
        /// </summary>
        /// <param name="forceUpdate">
        /// If <see langword="true"/>, triggers overriding IsNew and IsDirty. 
        /// If <see langword="false"/> then it is the same as calling Save().
        /// </param>
        /// <returns>A new object containing the saved values.</returns>
        /// <remarks>
        /// This overload is designed for use in web applications
        /// when implementing the Update method in your 
        /// data wrapper object.
        /// </remarks>
        public Csla.Core.BindableBase Save(bool forceUpdate)
        {
            if (forceUpdate && IsNew)
            {
                // mark the object as old - which makes it
                // not dirty
                MarkOld();
                // now mark the object as dirty so it can save
                MarkDirty(true);
            }
            return this.Save();
        }

        // public virtual void Update(SqlTransaction tr) { }
        public virtual void Update(List<DbCommand> cmdList)
        {
            if (!IsDirty)
                return;
            var updateNow = false;
            if (cmdList == null)
            {
                cmdList = new List<DbCommand>();
                updateNow = true;
            }
            var cm = new SqlCommand();

            if (IsNew && base.IsDirty)
                GenerateInsertCommand(cm);
            else if (base.IsDirty && !IsDeleted)
                GenerateUpdateCommand(cm);
            else if (!IsNew && IsDeleted)
                GenerateDeleteCommand(cm);
            if (!string.IsNullOrWhiteSpace(cm.CommandText))
            {         
               // WebServerHelper.UpdateData(cm);
                cmdList.Add(cm);
            }
            UpdateChildren(cmdList);
            if (updateNow)
            {
                DataServeice.BatchUpdateData(cmdList);
            }
        }
        /// <summary>
        /// this is Used for Update the children of this object.
        /// Used in Update()method.
        /// </summary>
        /// <param name="tr"></param>
        protected virtual void UpdateChildren(List<DbCommand> cmdList) { }
        #endregion


        #region Dayi

        public virtual void DeepCopyFromBO(BusinessBaseEx obj)
        {
            //obj.FieldManager.FieldData
        }


        public virtual BusinessBaseEx CopyToNew()
        {
            throw new NotImplementedException("BusinessBaseEx.CopyToNew");
        }


        #endregion




        public static Boolean SuspendBindingEvent = false;   //tianyun;
        #region BusinessRules, IsValid



        /// <summary>
        /// Override this method in your business class to
        /// be notified when you need to set up business
        /// rules.
        /// </summary>
        /// <remarks>
        /// This method is automatically called by CSLA .NET
        /// when your object should associate per-instance
        /// Business rules with its properties.
        /// </remarks>
        protected virtual void AddInstanceBusinessRules()
        {

        }

        /// <summary>
        /// Override this method in your business class to
        /// be notified when you need to set up shared 
        /// business rules.
        /// </summary>
        /// <remarks>
        /// This method is automatically called by CSLA .NET
        /// when your object should associate per-type 
        /// Business rules with its properties.
        /// </remarks>

        /// <summary>
        /// Returns <see langword="true" /> if the object is currently valid, <see langword="false" /> if the
        /// object has broken rules or is otherwise invalid.
        /// </summary>
        /// <remarks>
        /// <para>
        /// By default this property relies on the underling BusinessRules
        /// object to track whether any business rules are currently broken for this object.
        /// </para><para>
        /// You can override this property to provide more sophisticated
        /// implementations of the behavior. For instance, you should always override
        /// this method if your object has child objects, since the validity of this object
        /// is affected by the validity of all child objects.
        /// </para>
        /// </remarks>
        /// <returns>A value indicating if the object is currently valid.</returns>


        /// <summary>
        /// Provides access to the readonly collection of broken business rules
        /// for this object.
        /// </summary>
        /// <returns>A Csla.Business.RulesCollection object.</returns>
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Advanced)]
        public virtual BrokenRulesCollection BrokenRulesCollection
        {
            get { return BusinessRules.GetBrokenRules(); }
        }

        private void InitializeInstanceBusinessRules()
        {
            AddInstanceBusinessRules();

        }

        #endregion
        #region Header Added By Tianyun

        [NotUndoable()]
        BusinessBase _header = null;

        public virtual void SetHeader(BusinessBaseEx header)
        {
            //		  _isChild  = true;
            _header = header;
        }

        public BusinessBase Header
        {
            get { return _header; }
        }

        #endregion



        #region Editing control



        public void MarkAsNotChild()
        {
            _isChild = false;
        }


        public void ForceMarkAsChild()
        {
            MarkAsChild();
        }

        private bool _IsReturnSavedObj;
        public bool IsReturnSavedObj
        {
            get { return _IsReturnSavedObj; }
            set
            {
                _IsReturnSavedObj = value;
            }
        }
        #endregion

        public virtual void Clear() { }


        /// <summary>
        /// 判断子对象IsDirty.
        /// 越复杂的子对象应该排在参数的后面.
        /// </summary>
        /// <param name="childs"></param>
        /// <returns></returns>
        protected bool IsChildDirty(params IIsDirty[] childs)
        {
            foreach (var child in childs)
            {
                if (child != null && child.IsDirty)
                    return true;
            }
            return false;
        }




        public virtual string GetTableName() { return string.Empty; }

        public virtual object ToModel()
        {
            var pList = this.FieldManager.GetRegisteredProperties();
            var obj = new Dictionary<string, object>();

            foreach (var p in pList)
            {
                switch (p.Type.Name)
                {
                    case "DateTime":
                        obj.Add(p.Name, this.GetProperty(p).ToString());
                        break;
                    default:
                        obj.Add(p.Name, this.GetProperty(p));
                        break;
                }
            }
            return obj;
        }
        public object Get()
        {
            var obj = DataPortal.Fetch(this.GetType(), this) as BusinessBaseEx;
            obj.MarkOld();
            return obj;

        }

        public void Get(SafeDataReader dr)
        {
            var p = FieldManager.GetRegisteredProperties();
            foreach (var fd in p)
            {
                SetProperty(fd, dr.GetValue(fd.Name));
            }
            MarkOld();
        }
        protected virtual void DataPortal_Delete(object criteria)
        {
            Update(null);
        }
        protected new virtual void DataPortal_Insert()
        {
            Update(null);
        }
        protected new virtual void DataPortal_Update()
        {
            Update(null);
        }
        protected virtual void DataPortal_Fetch(object crit)
        {
            using (var cn = DbFactory.GetConnection(Database.CompanyConnection))
            {
               // cn.Open();
                var cm = cn.CreateCommand();

                GenerateSelectCommand(cm, crit);
                var dit = DataServeice.FecthData(cm);
                if (dit.Count == 0)
                    throw new ExceptionEx("记录未找到.");

                LoadDataFromDictionary(dit[0]);

                BusinessRules.CheckRules();
            }
        }

        public void LoadDataFromDictionary(IDictionary<string, object> dit)
        {
            foreach (var fd in this.FieldManager.GetRegisteredProperties())
            {

                var v = dit[fd.Name];
                if (fd.Type == typeof(bool))
                {
                    v = !object.Equals(v, "0");
                }
                SetProperty(fd, v);
            }
        }
        protected void GenerateSelectCommand(DbCommand pCommand, object crit)
        {


            var obj = crit as BusinessBaseEx;
            if (obj == null)
                return;
            var strWhere = new StringBuilder();
            var tem = string.Empty;
            var strTable = GetTableName();
            foreach (var fd in this.FieldManager.GetRegisteredProperties())
            {
                tem = string.Format("{0}", fd.Name);
                var p = this.FieldManager.GetRegisteredProperty(fd.Name);
                if (p.IsKey)
                {
                    strWhere.AppendFormat(" {0}={1}{0} And ", fd.Name, pPrefix);
                    pCommand.Parameters.AddWithValue(tem, obj.ReadProperty(fd));
                }
            }
            if (strWhere.Length > 0)
            {
                strWhere.Append(" 1=1");
                pCommand.CommandText = string.Format(" Select * From {0} Where {1}", strTable, strWhere);
            }
        }
        protected void GenerateDeleteCommand(DbCommand pCommand)
        {

            var strWhere = new StringBuilder();
            var tem = string.Empty;
            var strTable = GetTableName();
            foreach (Csla.Core.FieldManager.IFieldData fd in this.FieldManager.FieldData)
            {
                tem = string.Format("{0}", fd.Name);
                var p = this.FieldManager.GetRegisteredProperty(fd.Name);
                if (p.IsKey)
                {
                    strWhere.AppendFormat(" {0}={1}{0} And ", fd.Name, pPrefix);
                    pCommand.Parameters.AddWithValue(tem, fd.Value);
                }
            }
            if (strWhere.Length > 0)
            {
                strWhere.Append(" 1=1");
                pCommand.CommandText = string.Format(" Delete From {0} Where {1} ", strTable, strWhere);
            }
        }
        protected void GenerateUpdateCommand(DbCommand pCommand)
        {
            var sqlTxt = new StringBuilder();
            var strWhere = new StringBuilder();
            var tem = string.Empty;
            var strTable = GetTableName();
            foreach (Csla.Core.FieldManager.IFieldData fd in this.FieldManager.FieldData)
            {
                tem = string.Format("{0}", fd.Name);
                var p = this.FieldManager.GetRegisteredProperty(fd.Name);
                if (p.IsKey)
                {
                    strWhere.AppendFormat(" {0}={1}{0} And ", fd.Name, pPrefix);
                    pCommand.Parameters.AddWithValue(tem, fd.Value);
                }
                if (fd.IsDirty)
                {
                    sqlTxt.AppendFormat("{0} = {1}{0},", fd.Name, pPrefix);
                    var value = fd.Value;
                    if (fd.Value.Equals(true))
                    {
                        value = 1;
                    }
                    else if (fd.Value.Equals(false))
                    {
                        value = 0;
                    }
                    pCommand.Parameters.AddWithValue(tem, value);
                }
            }
            if (strWhere.Length > 0)
            {
                strWhere.Append(" 1=1 ");
                strWhere.Insert(0, "Where ");
                sqlTxt.Remove(sqlTxt.Length - 1, 1);
                pCommand.CommandText = string.Format(" Update {0} Set {1} {2}", strTable, sqlTxt, strWhere);
            }

        }
        protected void GenerateInsertCommand(DbCommand pCommand)
        {
            var sqlTxt = new StringBuilder();

            var str1 = string.Empty;
            var str2 = string.Empty;
            var tem = string.Empty;
            foreach (var fd in this.FieldManager.GetRegisteredProperties())
            {
                var objValue = GetProperty(fd);
                //自增列不能添加到语句中.
                if (fd.IsKey && fd.Type == typeof(int) && fd.DefaultValue.Equals(objValue))
                    continue;
                str1 += string.Format("{0},", fd.Name);
                tem = string.Format("{0}", fd.Name);
                str2 +=GlobalVar.Prefix+ tem + ",";
                var value = objValue;
                if (objValue.Equals(true))
                {
                    value = 1;
                }
                else if (objValue.Equals(false))
                {
                    value = 0;
                }
                pCommand.Parameters.AddWithValue(tem, value);
            }
            str1 = str1.TrimEnd(',');
            str2 = str2.TrimEnd(',');
            sqlTxt.AppendFormat("Insert Into {0} ({1}) Values ({2})", this.GetTableName(), str1, str2);
            pCommand.CommandText = sqlTxt.ToString();
        }

        public static string pPrefix
        {
           // get { return ":"; }//MySql or 其他
              get { return GlobalVar.Prefix; }//sqlServer
        }

    }

}
